package com.learning.batterySwapPro.common.exception;

import com.learning.batterySwapPro.common.response.ApiResponse;
import com.learning.batterySwapPro.common.response.ResultCode;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.dao.OptimisticLockingFailureException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.context.request.WebRequest;

import javax.security.sasl.AuthenticationException;
import java.nio.file.AccessDeniedException;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 全局异常处理
 *
 * @author wangyunyun
 */
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {


	/**
	 * 处理所有不可知的异常
	 */
	@ExceptionHandler(Exception.class)
	public ResponseEntity<ApiResponse<?>> handleException(Exception e, WebRequest request) {
		logError("系统异常", e, request);

		// 生产环境隐藏详细错误信息
		String errorMsg = isProdEnvironment() ? "服务异常，请稍后再试" : e.getMessage();

		return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
				.body(ApiResponse.fail(ResultCode.INTERNAL_SERVER_ERROR.getCode(), errorMsg));
	}

	/**
	 * 处理业务异常
	 */
	@ExceptionHandler(BusinessException.class)
	public ResponseEntity<ApiResponse<?>> handleBusinessException(BusinessException e, WebRequest request) {
		logError("业务异常", e, request);

		String errorMsg = StringUtils.isNotBlank(e.getCustomMessage()) ?
				e.getCustomMessage() :
				e.getErrorCode().getMessage();

		return ResponseEntity.status(e.getHttpStatus())
				.body(ApiResponse.fail(e.getErrorCode().getCode(), errorMsg));
	}

	/**
	 * 处理参数验证异常
	 */
	@ExceptionHandler(MethodArgumentNotValidException.class)
	public ResponseEntity<ApiResponse<?>> handleValidationException(MethodArgumentNotValidException e, WebRequest request) {
		logError("参数验证异常", e, request);

		// 提取所有字段错误
		List<FieldErrorDTO> fieldErrors = e.getBindingResult().getFieldErrors().stream()
				.map(error -> new FieldErrorDTO(
						error.getField(),
						error.getRejectedValue(),
						error.getDefaultMessage()))
				.collect(Collectors.toList());

		// 返回详细的字段错误信息
		return ResponseEntity.status(HttpStatus.BAD_REQUEST)
				.body(ApiResponse.fail(
						ResultCode.BAD_REQUEST.getCode(),
						"参数验证失败",
						fieldErrors));
	}

	/**
	 * 处理认证授权异常
	 */
	@ExceptionHandler({ AccessDeniedException.class, AuthenticationException.class})
	public ResponseEntity<ApiResponse<?>> handleAccessDeniedException(Exception e, WebRequest request) {
		logError("访问拒绝异常", e, request);

		return ResponseEntity.status(HttpStatus.FORBIDDEN)
				.body(ApiResponse.fail(
						ResultCode.FORBIDDEN.getCode(),
						"没有访问权限，请联系管理员"));
	}

	/**
	 * 处理资源不存在异常
	 */
	@ExceptionHandler(ResourceNotFoundException.class)
	public ResponseEntity<ApiResponse<?>> handleResourceNotFound(ResourceNotFoundException e, WebRequest request) {
		logError("资源未找到", e, request);

		return ResponseEntity.status(HttpStatus.NOT_FOUND)
				.body(ApiResponse.fail(
						ResultCode.NOT_FOUND.getCode(),
						e.getMessage()));
	}

	/**
	 * 处理并发冲突异常
	 */
	@ExceptionHandler(OptimisticLockingFailureException.class)
	public ResponseEntity<ApiResponse<?>> handleConcurrencyConflict(OptimisticLockingFailureException e, WebRequest request) {
		logError("并发冲突异常", e, request);

		return ResponseEntity.status(HttpStatus.CONFLICT)
				.body(ApiResponse.fail(
						ResultCode.CONCURRENCY_CONFLICT.getCode(),
						"数据已被修改，请刷新后重试"));
	}

	/**
	 * 统一记录错误日志
	 */
	private void logError(String errorType, Exception e, WebRequest request) {
		String requestPath = request.getDescription(false).replace("uri=", "");

		log.error("{} - Path: {}", errorType, requestPath, e);

		// 记录额外上下文信息（生产环境可发送到监控系统）
		logContextInfo(request, e);
	}

	/**
	 * 记录上下文信息
	 */
	private void logContextInfo(WebRequest request, Exception e) {
		// 实现略 - 可记录用户信息、请求参数等
	}

	private boolean isProdEnvironment() {
		// 实现略 - 判断当前环境
		return false;
	}
}
